//! DSL parser implementation
//! 
//! This module provides parsing capabilities for YAML and JSON test case definitions,
//! with full HttpRunner compatibility.

use crate::error::{Error, Result};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;

/// DSL parser for test case definitions
pub struct DSLParser {
    /// Parser configuration
    config: ParserConfig,
}

/// Parser configuration
#[derive(Debug, Clone)]
pub struct ParserConfig {
    /// Allow unknown fields in parsing
    pub allow_unknown_fields: bool,
    
    /// Strict mode for validation
    pub strict_mode: bool,
    
    /// Maximum nesting depth
    pub max_depth: usize,
}

impl Default for ParserConfig {
    fn default() -> Self {
        Self {
            allow_unknown_fields: true,
            strict_mode: false,
            max_depth: 10,
        }
    }
}

/// Test case definition structure (compatible with HttpRunner)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TestCaseDefinition {
    /// Test case configuration
    pub config: ConfigDefinition,
    
    /// Test steps
    pub teststeps: Vec<StepDefinition>,
    
    /// Optional metadata
    #[serde(skip_serializing_if = "Option::is_none")]
    pub metadata: Option<Value>,
}

/// Configuration definition
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigDefinition {
    /// Test case name
    pub name: String,
    
    /// Base URL for requests
    #[serde(skip_serializing_if = "Option::is_none")]
    pub base_url: Option<String>,
    
    /// Global variables
    #[serde(skip_serializing_if = "Option::is_none")]
    pub variables: Option<HashMap<String, Value>>,
    
    /// Global headers
    #[serde(skip_serializing_if = "Option::is_none")]
    pub headers: Option<HashMap<String, String>>,
    
    /// SSL verification
    #[serde(skip_serializing_if = "Option::is_none")]
    pub verify: Option<bool>,
    
    /// Request timeout
    #[serde(skip_serializing_if = "Option::is_none")]
    pub timeout: Option<f64>,
    
    /// Export variables
    #[serde(skip_serializing_if = "Option::is_none")]
    pub export: Option<Vec<String>>,
    
    /// Setup hooks
    #[serde(skip_serializing_if = "Option::is_none")]
    pub setup_hooks: Option<Vec<String>>,
    
    /// Teardown hooks
    #[serde(skip_serializing_if = "Option::is_none")]
    pub teardown_hooks: Option<Vec<String>>,

    /// Test environment
    #[serde(skip_serializing_if = "Option::is_none")]
    pub env: Option<String>,

    /// Test parameters (for parameterized tests)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub parameters: Option<Vec<HashMap<String, Value>>>,

    /// Test tags
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tags: Option<Vec<String>>,

    /// Test priority
    #[serde(skip_serializing_if = "Option::is_none")]
    pub priority: Option<u8>,

    /// Test weight (for load testing)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub weight: Option<u32>,

    /// Parallel execution configuration
    #[serde(skip_serializing_if = "Option::is_none")]
    pub parallel: Option<ParallelConfig>,

    /// Additional configuration
    #[serde(flatten)]
    pub additional: HashMap<String, Value>,
}

/// Parallel execution configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ParallelConfig {
    /// Enable parallel execution
    pub enabled: bool,

    /// Maximum concurrent workers
    #[serde(default = "default_max_workers")]
    pub max_workers: u32,

    /// Worker timeout in seconds
    #[serde(default = "default_worker_timeout")]
    pub worker_timeout: u64,
}

fn default_max_workers() -> u32 {
    4
}

fn default_worker_timeout() -> u64 {
    300
}

/// Step definition structure
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StepDefinition {
    /// Step name
    pub name: String,

    /// Step variables
    #[serde(skip_serializing_if = "Option::is_none")]
    pub variables: Option<HashMap<String, Value>>,

    /// HTTP request definition
    #[serde(skip_serializing_if = "Option::is_none")]
    pub request: Option<Value>,

    /// API reference
    #[serde(skip_serializing_if = "Option::is_none")]
    pub api: Option<Value>,

    /// Test case reference
    #[serde(skip_serializing_if = "Option::is_none")]
    pub testcase: Option<Value>,

    /// Transaction definition
    #[serde(skip_serializing_if = "Option::is_none")]
    pub transaction: Option<Value>,

    /// Conditional execution
    #[serde(skip_serializing_if = "Option::is_none")]
    pub if_condition: Option<String>,

    /// Loop configuration
    #[serde(skip_serializing_if = "Option::is_none")]
    pub loop_config: Option<LoopConfig>,

    /// Retry configuration
    #[serde(skip_serializing_if = "Option::is_none")]
    pub retry: Option<RetryConfig>,

    /// Timeout for this step (in seconds)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub timeout: Option<u64>,

    /// Skip this step
    #[serde(skip_serializing_if = "Option::is_none")]
    pub skip: Option<bool>,
    
    /// Think time definition
    #[serde(skip_serializing_if = "Option::is_none")]
    pub think_time: Option<Value>,
    
    /// WebSocket action
    #[serde(skip_serializing_if = "Option::is_none")]
    pub websocket: Option<Value>,
    
    /// Rendezvous point
    #[serde(skip_serializing_if = "Option::is_none")]
    pub rendezvous: Option<Value>,
    
    /// Setup hooks
    #[serde(skip_serializing_if = "Option::is_none")]
    pub setup_hooks: Option<Vec<String>>,
    
    /// Teardown hooks
    #[serde(skip_serializing_if = "Option::is_none")]
    pub teardown_hooks: Option<Vec<String>>,
    
    /// Variable extraction rules
    #[serde(skip_serializing_if = "Option::is_none")]
    pub extract: Option<HashMap<String, Value>>,
    
    /// Validation rules
    #[serde(skip_serializing_if = "Option::is_none")]
    pub validate: Option<Vec<Value>>,
    
    /// Export variables
    #[serde(skip_serializing_if = "Option::is_none")]
    pub export: Option<Vec<String>>,
    
    /// Additional step data
    #[serde(flatten)]
    pub additional: HashMap<String, Value>,
}

impl DSLParser {
    /// Create a new DSL parser
    pub fn new() -> Self {
        Self {
            config: ParserConfig::default(),
        }
    }
    
    /// Create a new DSL parser with custom configuration
    pub fn with_config(config: ParserConfig) -> Self {
        Self { config }
    }
    
    /// Parse YAML content into test case definition
    pub fn parse_yaml(&self, content: &str) -> Result<TestCaseDefinition> {
        serde_yaml::from_str(content)
            .map_err(|e| Error::Internal(format!("YAML parsing error: {}", e)))
    }
    
    /// Parse JSON content into test case definition
    pub fn parse_json(&self, content: &str) -> Result<TestCaseDefinition> {
        serde_json::from_str(content)
            .map_err(|e| Error::Internal(format!("JSON parsing error: {}", e)))
    }
    
    /// Parse from generic Value
    pub fn parse_value(&self, value: &Value) -> Result<TestCaseDefinition> {
        serde_json::from_value(value.clone())
            .map_err(|e| Error::Internal(format!("Value parsing error: {}", e)))
    }
    
    /// Validate test case definition structure
    pub fn validate(&self, definition: &TestCaseDefinition) -> Result<()> {
        // Validate config
        if definition.config.name.is_empty() {
            return Err(Error::Internal("Test case name cannot be empty".to_string()));
        }
        
        // Validate steps
        if definition.teststeps.is_empty() {
            return Err(Error::Internal("Test case must have at least one step".to_string()));
        }
        
        for (index, step) in definition.teststeps.iter().enumerate() {
            self.validate_step(step, index)?;
        }
        
        Ok(())
    }
    
    /// Validate a single step definition
    fn validate_step(&self, step: &StepDefinition, index: usize) -> Result<()> {
        if step.name.is_empty() {
            return Err(Error::Internal(format!("Step {} name cannot be empty", index)));
        }

        // Check that step has at least one action (unless it's skipped)
        if step.skip != Some(true) {
            let has_action = step.request.is_some()
                || step.api.is_some()
                || step.testcase.is_some()
                || step.transaction.is_some()
                || step.think_time.is_some()
                || step.websocket.is_some()
                || step.rendezvous.is_some();

            if !has_action {
                return Err(Error::Internal(format!("Step {} must have at least one action", index)));
            }
        }

        // Validate loop configuration
        if let Some(loop_config) = &step.loop_config {
            self.validate_loop_config(loop_config, index)?;
        }

        // Validate retry configuration
        if let Some(retry_config) = &step.retry {
            self.validate_retry_config(retry_config, index)?;
        }

        Ok(())
    }

    /// Validate loop configuration
    fn validate_loop_config(&self, loop_config: &LoopConfig, step_index: usize) -> Result<()> {
        match loop_config.loop_type {
            LoopType::Count => {
                if loop_config.count.is_none() {
                    return Err(Error::Internal(format!("Step {} count loop requires count parameter", step_index)));
                }
            }
            LoopType::Data => {
                if loop_config.data.is_none() {
                    return Err(Error::Internal(format!("Step {} data loop requires data parameter", step_index)));
                }
                if loop_config.variable.is_none() {
                    return Err(Error::Internal(format!("Step {} data loop requires variable parameter", step_index)));
                }
            }
            LoopType::While => {
                if loop_config.condition.is_none() {
                    return Err(Error::Internal(format!("Step {} while loop requires condition parameter", step_index)));
                }
            }
            LoopType::Range => {
                // Range loops can use count or data
                if loop_config.count.is_none() && loop_config.data.is_none() {
                    return Err(Error::Internal(format!("Step {} range loop requires count or data parameter", step_index)));
                }
            }
        }

        if loop_config.max_iterations == 0 {
            return Err(Error::Internal(format!("Step {} loop max_iterations must be greater than 0", step_index)));
        }

        Ok(())
    }

    /// Validate retry configuration
    fn validate_retry_config(&self, retry_config: &RetryConfig, step_index: usize) -> Result<()> {
        if retry_config.max_attempts == 0 {
            return Err(Error::Internal(format!("Step {} retry max_attempts must be greater than 0", step_index)));
        }

        if retry_config.multiplier <= 0.0 {
            return Err(Error::Internal(format!("Step {} retry multiplier must be greater than 0", step_index)));
        }

        if retry_config.max_delay_ms < retry_config.delay_ms {
            return Err(Error::Internal(format!("Step {} retry max_delay_ms must be >= delay_ms", step_index)));
        }

        Ok(())
    }
    
    /// Convert test case definition to YAML string
    pub fn to_yaml(&self, definition: &TestCaseDefinition) -> Result<String> {
        serde_yaml::to_string(definition)
            .map_err(|e| Error::Internal(format!("YAML serialization error: {}", e)))
    }
    
    /// Convert test case definition to JSON string
    pub fn to_json(&self, definition: &TestCaseDefinition) -> Result<String> {
        serde_json::to_string_pretty(definition)
            .map_err(|e| Error::Internal(format!("JSON serialization error: {}", e)))
    }
    
    /// Get parser configuration
    pub fn config(&self) -> &ParserConfig {
        &self.config
    }
    
    /// Set parser configuration
    pub fn set_config(&mut self, config: ParserConfig) {
        self.config = config;
    }

    /// Parse and expand parameterized test cases
    pub fn expand_parameterized(&self, definition: &TestCaseDefinition) -> Result<Vec<TestCaseDefinition>> {
        if let Some(parameters) = &definition.config.parameters {
            let mut expanded_cases = Vec::new();

            for (index, param_set) in parameters.iter().enumerate() {
                let mut expanded_case = definition.clone();

                // Update test case name to include parameter index
                expanded_case.config.name = format!("{}_param_{}", definition.config.name, index);

                // Merge parameters into global variables
                if expanded_case.config.variables.is_none() {
                    expanded_case.config.variables = Some(HashMap::new());
                }

                if let Some(ref mut variables) = expanded_case.config.variables {
                    for (key, value) in param_set {
                        variables.insert(key.clone(), value.clone());
                    }
                }

                expanded_cases.push(expanded_case);
            }

            Ok(expanded_cases)
        } else {
            Ok(vec![definition.clone()])
        }
    }

    /// Evaluate condition expression (basic implementation)
    pub fn evaluate_condition(&self, condition: &str, variables: &HashMap<String, Value>) -> Result<bool> {
        // This is a simplified condition evaluator
        // In a full implementation, you'd want a proper expression parser

        // Handle simple equality checks: ${var} == "value"
        if condition.contains("==") {
            let parts: Vec<&str> = condition.split("==").collect();
            if parts.len() == 2 {
                let left = parts[0].trim();
                let right = parts[1].trim().trim_matches('"');

                // Extract variable name from ${var} syntax
                if left.starts_with("${") && left.ends_with("}") {
                    let var_name = &left[2..left.len()-1];
                    if let Some(var_value) = variables.get(var_name) {
                        if let Some(str_value) = var_value.as_str() {
                            return Ok(str_value == right);
                        }
                    }
                }
            }
        }

        // Handle simple boolean variables: ${enabled}
        if condition.starts_with("${") && condition.ends_with("}") {
            let var_name = &condition[2..condition.len()-1];
            if let Some(var_value) = variables.get(var_name) {
                if let Some(bool_value) = var_value.as_bool() {
                    return Ok(bool_value);
                }
            }
        }

        // Default to false for unknown conditions
        Ok(false)
    }

    /// Check if a step should be skipped based on conditions
    pub fn should_skip_step(&self, step: &StepDefinition, variables: &HashMap<String, Value>) -> Result<bool> {
        // Check explicit skip flag
        if step.skip == Some(true) {
            return Ok(true);
        }

        // Check conditional execution
        if let Some(condition) = &step.if_condition {
            return Ok(!self.evaluate_condition(condition, variables)?);
        }

        Ok(false)
    }
}

impl Default for DSLParser {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_parser_creation() {
        let parser = DSLParser::new();
        assert!(!parser.config.strict_mode);
        assert!(parser.config.allow_unknown_fields);
    }

    #[test]
    fn test_yaml_parsing() {
        let parser = DSLParser::new();
        let yaml_content = r#"
config:
  name: "Test Case"
  base_url: "https://api.example.com"
  variables:
    user_id: 123
    token: "abc123"

teststeps:
  - name: "Get User"
    request:
      method: "GET"
      url: "/users/${user_id}"
      headers:
        Authorization: "Bearer ${token}"
    validate:
      - check: "status_code"
        expect: 200
"#;
        
        let result = parser.parse_yaml(yaml_content);
        assert!(result.is_ok());
        
        let definition = result.unwrap();
        assert_eq!(definition.config.name, "Test Case");
        assert_eq!(definition.config.base_url, Some("https://api.example.com".to_string()));
        assert_eq!(definition.teststeps.len(), 1);
        assert_eq!(definition.teststeps[0].name, "Get User");
    }

    #[test]
    fn test_json_parsing() {
        let parser = DSLParser::new();
        let json_content = r#"
{
  "config": {
    "name": "Test Case",
    "base_url": "https://api.example.com",
    "variables": {
      "user_id": 123,
      "token": "abc123"
    }
  },
  "teststeps": [
    {
      "name": "Get User",
      "request": {
        "method": "GET",
        "url": "/users/${user_id}",
        "headers": {
          "Authorization": "Bearer ${token}"
        }
      },
      "validate": [
        {
          "check": "status_code",
          "expect": 200
        }
      ]
    }
  ]
}
"#;
        
        let result = parser.parse_json(json_content);
        assert!(result.is_ok());
        
        let definition = result.unwrap();
        assert_eq!(definition.config.name, "Test Case");
        assert_eq!(definition.teststeps.len(), 1);
    }

    #[test]
    fn test_validation() {
        let parser = DSLParser::new();
        let mut definition = TestCaseDefinition {
            config: ConfigDefinition {
                name: "Test".to_string(),
                base_url: None,
                variables: None,
                headers: None,
                verify: None,
                timeout: None,
                export: None,
                setup_hooks: None,
                teardown_hooks: None,
                env: None,
                parameters: None,
                tags: None,
                priority: None,
                weight: None,
                parallel: None,
                additional: HashMap::new(),
            },
            teststeps: vec![
                StepDefinition {
                    name: "Step 1".to_string(),
                    variables: None,
                    request: Some(serde_json::json!({"method": "GET", "url": "/test"})),
                    api: None,
                    testcase: None,
                    transaction: None,
                    if_condition: None,
                    loop_config: None,
                    retry: None,
                    timeout: None,
                    skip: None,
                    think_time: None,
                    websocket: None,
                    rendezvous: None,
                    setup_hooks: None,
                    teardown_hooks: None,
                    extract: None,
                    validate: None,
                    export: None,
                    additional: HashMap::new(),
                }
            ],
            metadata: None,
        };
        
        // Valid definition
        assert!(parser.validate(&definition).is_ok());
        
        // Invalid: empty name
        definition.config.name = "".to_string();
        assert!(parser.validate(&definition).is_err());
        
        // Invalid: no steps
        definition.config.name = "Test".to_string();
        definition.teststeps.clear();
        assert!(parser.validate(&definition).is_err());
    }

    #[test]
    fn test_serialization() {
        let parser = DSLParser::new();
        let definition = TestCaseDefinition {
            config: ConfigDefinition {
                name: "Test Case".to_string(),
                base_url: Some("https://api.example.com".to_string()),
                variables: Some({
                    let mut vars = HashMap::new();
                    vars.insert("user_id".to_string(), serde_json::json!(123));
                    vars
                }),
                headers: None,
                verify: None,
                timeout: None,
                export: None,
                setup_hooks: None,
                teardown_hooks: None,
                env: None,
                parameters: None,
                tags: None,
                priority: None,
                weight: None,
                parallel: None,
                additional: HashMap::new(),
            },
            teststeps: vec![
                StepDefinition {
                    name: "Get User".to_string(),
                    variables: None,
                    request: Some(serde_json::json!({
                        "method": "GET",
                        "url": "/users/${user_id}"
                    })),
                    api: None,
                    testcase: None,
                    transaction: None,
                    if_condition: None,
                    loop_config: None,
                    retry: None,
                    timeout: None,
                    skip: None,
                    think_time: None,
                    websocket: None,
                    rendezvous: None,
                    setup_hooks: None,
                    teardown_hooks: None,
                    extract: None,
                    validate: None,
                    export: None,
                    additional: HashMap::new(),
                }
            ],
            metadata: None,
        };
        
        // Test YAML serialization
        let yaml_result = parser.to_yaml(&definition);
        assert!(yaml_result.is_ok());
        
        // Test JSON serialization
        let json_result = parser.to_json(&definition);
        assert!(json_result.is_ok());
        
        // Test round-trip
        let yaml_str = yaml_result.unwrap();
        let parsed_back = parser.parse_yaml(&yaml_str);
        assert!(parsed_back.is_ok());
    }
}

/// Loop configuration for step execution
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LoopConfig {
    /// Loop type
    pub loop_type: LoopType,

    /// Loop count (for count-based loops)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub count: Option<u32>,

    /// Loop data (for data-driven loops)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub data: Option<Vec<Value>>,

    /// Loop variable name (for data-driven loops)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub variable: Option<String>,

    /// Loop condition (for while loops)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub condition: Option<String>,

    /// Maximum iterations (safety limit)
    #[serde(default = "default_max_iterations")]
    pub max_iterations: u32,
}

/// Loop types
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum LoopType {
    /// Fixed count loop
    Count,
    /// Data-driven loop
    Data,
    /// Condition-based loop
    While,
    /// Range-based loop
    Range,
}

/// Retry configuration for step execution
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RetryConfig {
    /// Maximum retry attempts
    pub max_attempts: u32,

    /// Retry delay in milliseconds
    #[serde(default = "default_retry_delay")]
    pub delay_ms: u64,

    /// Retry delay multiplier (for exponential backoff)
    #[serde(default = "default_retry_multiplier")]
    pub multiplier: f64,

    /// Maximum delay in milliseconds
    #[serde(default = "default_max_delay")]
    pub max_delay_ms: u64,

    /// Retry conditions (when to retry)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub retry_on: Option<Vec<RetryCondition>>,
}

/// Retry conditions
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RetryCondition {
    /// Condition type
    #[serde(rename = "type")]
    pub condition_type: String,

    /// Status codes to retry on
    #[serde(skip_serializing_if = "Option::is_none")]
    pub status_codes: Option<Vec<u16>>,

    /// Error message pattern
    #[serde(skip_serializing_if = "Option::is_none")]
    pub error_message: Option<String>,

    /// Custom condition expression
    #[serde(skip_serializing_if = "Option::is_none")]
    pub expression: Option<String>,
}

/// Default values for configuration
fn default_max_iterations() -> u32 {
    1000
}

fn default_retry_delay() -> u64 {
    1000
}

fn default_retry_multiplier() -> f64 {
    2.0
}

fn default_max_delay() -> u64 {
    30000
}
